package top.bug.blog.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.bug.blog.common.BasePO;
import top.bug.blog.common.UserContext;
import top.bug.blog.constant.CommonConstants;
import top.bug.blog.constant.UserConstants;
import top.bug.blog.entity.dto.common.BatchRequest;
import top.bug.blog.entity.dto.user.UserAddRequest;
import top.bug.blog.entity.dto.user.UserQueryRequest;
import top.bug.blog.entity.dto.user.UserResetPwdRequest;
import top.bug.blog.entity.dto.user.UserUpdateRequest;
import top.bug.blog.entity.po.User;
import top.bug.blog.entity.vo.user.LoginUserVO;
import top.bug.blog.entity.vo.user.SafeUserVO;
import top.bug.blog.enums.ErrorCode;
import top.bug.blog.exception.BussinessException;
import top.bug.blog.mapper.UserMapper;
import top.bug.blog.service.UserService;
import top.bug.blog.utils.PasswordUtil;
import top.bug.blog.utils.QueryWrapperUtils;
import top.bug.blog.utils.StrUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.stream.Collectors;

import static top.bug.blog.utils.StrUtils.isNotBlank;

/**
 * @author <a href="https://github.com/jiusi66">九思.</a>
 * @email 10837907@qq.com
 * @from <a href="https://www.52bug.top">递归茶馆-www.52bug.top</a>
 * @description 用户 实现类
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private HttpSession session;

    // region 登录注销

    @Override
    public LoginUserVO userLogin(String username, String password) {
        // 根据用户名密码获取用户信息
        User userPo = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getUsername, username));
        if (null == userPo || !PasswordUtil.verify(password, userPo.getPassword())) {
            throw new BussinessException(ErrorCode.USER_LOGIN_ERROR);
        }
        if (CommonConstants.STATUS_DISABLE.equals(userPo.getStatus())) {
            throw new BussinessException(ErrorCode.USER_ACCOUNT_FORBIDDEN);
        }
        // 保存用户信息到 session
        session.setAttribute(UserConstants.USER_LOGIN_KEY, userPo.getId());
        session.setMaxInactiveInterval(UserConstants.LOGIN_EXPIRE_TIME);
        return getLoginUserVO(userPo);
    }

    @Override
    public Boolean logout() {
        session.removeAttribute(UserConstants.USER_LOGIN_KEY);
        return true;
    }

    // endregion

    // region 增删改查

    private LoginUserVO getLoginUserVO(User userPo) {
        if (userPo == null) {
            return null;
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        BeanUtils.copyProperties(userPo, loginUserVO);
        return loginUserVO;
    }

    @Override
    public LoginUserVO getCurrentUserInfo() {
        String userId = UserContext.getUserId();
        User userPo = userMapper.selectById(userId);
        return getLoginUserVO(userPo);
    }

    @Override
    public Page<SafeUserVO> getUserList(UserQueryRequest queryRequest) {
        if (null == queryRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        Page<User> page = new Page<>(queryRequest.getPageNum(), queryRequest.getPageSize());
        String username = queryRequest.getUsername();
        String nickname = queryRequest.getNickname();
        String phone = queryRequest.getPhone();
        String email = queryRequest.getEmail();
        String status = queryRequest.getStatus();
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
                .like(isNotBlank(username), User::getUsername, username)
                .like(isNotBlank(nickname), User::getNickname, nickname)
                .like(isNotBlank(phone), User::getPhone, phone)
                .like(isNotBlank(email), User::getEmail, email)
                .eq(isNotBlank(status), User::getStatus, status);
        // 时间范围参数
        QueryWrapperUtils.addTimeRangeCondition(queryWrapper, queryRequest.getCreateTimeRange(), User::getCreateOn);
        QueryWrapperUtils.addTimeRangeCondition(queryWrapper, queryRequest.getUpdateTimeRange(), User::getUpdateOn);
        userMapper.selectPage(page, queryWrapper);
        // 转换并脱敏用户信息
        List<SafeUserVO> safeList = page.getRecords().stream().map(this::convertToSafeUserVO)
                .collect(Collectors.toList());
        // 显式构建安全分页对象，避免BeanUtil潜在问题
        Page<SafeUserVO> safePage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal(), page.searchCount());
        safePage.setRecords(safeList);
        // 设置总页数
        safePage.setPages(page.getPages());
        return safePage;
    }

    /**
     * 封装脱敏转换逻辑
     */
    private SafeUserVO convertToSafeUserVO(User user) {
        return new SafeUserVO.Builder()
                .from(user)
                .build();
    }

    @Override
    public Boolean add(UserAddRequest addRequest) {
        // 必填项校验
        String username = addRequest.getUsername();
        String password = addRequest.getPassword();
        String nickname = addRequest.getNickname();
        if (StrUtils.isAnyBlank(username, password, nickname)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        User user = new User();
        BeanUtil.copyProperties(addRequest, user);
        // 默认值填充
        user.setStatus(CommonConstants.STATUS_ENABLE);
        // 密码加密
        user.setPassword(PasswordUtil.encrypt(password));
        // 保存用户（唯一性由数据库层面进行控制）
        boolean save = false;
        try {
            save = this.save(user);
        } catch (DuplicateKeyException e) {
            throw new BussinessException(ErrorCode.DATA_ALREADY_EXISTED);
        }
        return save;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(BatchRequest batchRequest) {
        if (null == batchRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        List<String> ids = batchRequest.getIds();
        if (CollUtil.isEmpty(ids)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        return this.removeBatchByIds(ids);
    }

    @Override
    public Boolean edit(UserUpdateRequest updateRequest) {
        if (null == updateRequest || StrUtil.isEmpty(updateRequest.getId())) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        User user = new User();
        BeanUtils.copyProperties(updateRequest, user);
        return this.updateById(user);
    }

    @Override
    public Boolean resetPwd(UserResetPwdRequest resetPwdRequest) {
        // 参数校验
        if (null == resetPwdRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String id = resetPwdRequest.getId();
        String oldPassword = resetPwdRequest.getOldPassword();
        String password = resetPwdRequest.getPassword();
        if (StrUtils.isAnyBlank(id, oldPassword, password)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        User user = this.getById(id);
        if (user == null) {
            throw new BussinessException(ErrorCode.RESULT_DATA_NONE);
        }
        // 旧密码校验
        if (!PasswordUtil.verify(oldPassword, user.getPassword())) {
            throw new BussinessException("原密码输入错误！");
        }
        if (oldPassword.equals(password)) {
            throw new BussinessException("新密码不能和原密码相同！");
        }
        // 生成新密码哈希
        String encryptedNewPwd = PasswordUtil.encrypt(password);
        // 密码修改
        return this.update(new LambdaUpdateWrapper<User>().set(User::getPassword, encryptedNewPwd).eq(BasePO::getId, id));
    }

    // endregion
}